package com.bstek.urule.console.servlet.respackage;

import com.bstek.urule.Configure;
import com.bstek.urule.RuleException;
import com.bstek.urule.Utils;
import com.bstek.urule.builder.KnowledgeBase;
import com.bstek.urule.builder.KnowledgeBuilder;
import com.bstek.urule.builder.ResourceBase;
import com.bstek.urule.console.servlet.base.BaseController;
import com.bstek.urule.console.EnvironmentUtils;
import com.bstek.urule.console.User;
import com.bstek.urule.console.repository.ClientConfig;
import com.bstek.urule.console.repository.RepositoryService;
import com.bstek.urule.console.repository.RepositoryServiceImpl;
import com.bstek.urule.console.repository.model.ResourcePackage;
import com.bstek.urule.console.servlet.RequestContext;
import com.bstek.urule.controller.KnowledgePackageReceiverController;
import com.bstek.urule.model.GeneralEntity;
import com.bstek.urule.model.flow.FlowDefinition;
import com.bstek.urule.model.library.Datatype;
import com.bstek.urule.model.library.variable.Variable;
import com.bstek.urule.model.library.variable.VariableCategory;
import com.bstek.urule.model.rule.RuleInfo;
import com.bstek.urule.runtime.KnowledgePackage;
import com.bstek.urule.runtime.KnowledgePackageWrapper;
import com.bstek.urule.runtime.KnowledgeSession;
import com.bstek.urule.runtime.KnowledgeSessionFactory;
import com.bstek.urule.runtime.cache.CacheUtils;
import com.bstek.urule.runtime.response.ExecutionResponse;
import com.bstek.urule.runtime.response.ExecutionResponseImpl;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.*;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.*;

/**
 * <p>
 *
 * </p>
 *
 * @author zhouchaoliang
 * @since 2021/12/9
 */
@Controller
public class PackageController extends BaseController {
    public static final String KB_KEY="_kb";
    public static final String VCS_KEY="_vcs";
    public static final String IMPORT_EXCEL_DATA="_import_excel_data";
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private KnowledgeBuilder knowledgeBuilder;
    @Autowired
    private HttpSessionKnowledgeCache httpSessionKnowledgeCache;

    @GetMapping("/urule/packageeditor")
    public ModelAndView packageeditor(HttpServletRequest req, HttpServletResponse resp) {
        ModelAndView mv = new ModelAndView("/package-editor");
        mv.addObject("contextPath", req.getContextPath());
        return mv;
    }

    @ResponseBody
    @PostMapping("/urule/packageeditor/loadPackages")
    public List<ResourcePackage> loadPackages(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        String project=req.getParameter("project");
        project= Utils.decodeURL(project);
        return repositoryService.loadProjectResourcePackages(project);
    }

    @PostMapping("/urule/packageeditor/exportExcelTemplate")
    @SuppressWarnings("unchecked")
    public void exportExcelTemplate(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        List<VariableCategory> variableCategories=(List<VariableCategory>)httpSessionKnowledgeCache.get(req, VCS_KEY);
        if(variableCategories==null){
            KnowledgeBase knowledgeBase=buildKnowledgeBase(req);
            variableCategories=knowledgeBase.getResourceLibrary().getVariableCategories();
        }
        SXSSFWorkbook wb = new SXSSFWorkbook();
        XSSFCellStyle style=(XSSFCellStyle)wb.createCellStyle();
        Color c=new Color(147,208,15);
        XSSFColor xssfColor=new XSSFColor(c);
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        style.setFillForegroundColor(xssfColor);
        for(VariableCategory vc:variableCategories){
            buildSheet(wb, vc,style);
        }
        resp.setContentType("application/x-xls");
        resp.setHeader("Content-Disposition","attachment; filename=urule-batch-test-template.xlsx");
        OutputStream outputStream=resp.getOutputStream();
        wb.write(outputStream);;
        outputStream.flush();
        outputStream.close();
    }


    @PostMapping("/urule/packageeditor/importExcelTemplate")
    public void importExcelTemplate(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        DiskFileItemFactory factory=new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        List<FileItem> items = upload.parseRequest(req);
        Iterator<FileItem> itr = items.iterator();
        List<Map<String,Object>> mapData=null;
        while (itr.hasNext()) {
            FileItem item = (FileItem) itr.next();
            String name=item.getFieldName();
            if(!name.equals("file")){
                continue;
            }
            InputStream stream=item.getInputStream();
            mapData=parseExcel(stream);
            httpSessionKnowledgeCache.put(req, IMPORT_EXCEL_DATA, mapData);
            stream.close();
            break;
        }
        httpSessionKnowledgeCache.put(req, IMPORT_EXCEL_DATA, mapData);
        writeObjectToJson(resp, mapData);
    }

    @ResponseBody
    @PostMapping("/urule/packageeditor/loadFlows")
    public Collection<FlowDefinition> loadFlows(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        KnowledgeBase knowledgeBase=(KnowledgeBase)httpSessionKnowledgeCache.get(req, KB_KEY);
        return knowledgeBase.getFlowMap().values();
    }

    @PostMapping("/urule/packageeditor/pushKnowledgePackageToClients")
    public void pushKnowledgePackageToClients(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        String project=req.getParameter("project");
        project=Utils.decodeURL(project);
        String packageId=project+"/"+Utils.decodeURL(req.getParameter("packageId"));
        if(packageId.startsWith("/")){
            packageId=packageId.substring(1,packageId.length());
        }
        KnowledgePackage knowledgePackage= CacheUtils.getKnowledgeCache().getKnowledge(packageId);

        ObjectMapper mapper=new ObjectMapper();
        mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
        mapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS,false);
        mapper.setDateFormat(new SimpleDateFormat(Configure.getDateFormat()));
        StringWriter writer=new StringWriter();
        mapper.writeValue(writer, new KnowledgePackageWrapper(knowledgePackage));
        String content=writer.getBuffer().toString();
        writer.close();
        StringBuffer sb=new StringBuffer();
        List<ClientConfig> clients=repositoryService.loadClientConfigs(project);
        int i=0;
        for(ClientConfig config:clients){
            if(i>0){
                sb.append("<br>");
            }
            boolean result=pushKnowledgePackage(packageId,content,config.getClient());
            if(result){
                sb.append("<span class=\"text-info\" style='line-height:30px'>推送到客户端："+config.getName()+"："+config.getClient()+" 成功</span>");
            }else{
                sb.append("<span class=\"text-danger\" style='line-height:30px'>推送到客户端："+config.getName()+"："+config.getClient()+" 失败</span>");
            }
            i++;
        }
        Map<String,Object> map=new HashMap<String,Object>();
        map.put("info", sb.toString());
        writeObjectToJson(resp, map);
    }

    @PostMapping("/urule/packageeditor/refreshKnowledgeCache")
    public void refreshKnowledgeCache(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        String project=req.getParameter("project");
        project=Utils.decodeURL(project);
        String packageId=project+"/"+Utils.decodeURL(req.getParameter("packageId"));
        if(packageId.startsWith("/")){
            packageId=packageId.substring(1,packageId.length());
        }
        KnowledgeBase knowledgeBase= buildKnowledgeBase(req);
        KnowledgePackage knowledgePackage=knowledgeBase.getKnowledgePackage();
        CacheUtils.getKnowledgeCache().putKnowledge(packageId, knowledgePackage);
        Map<String,Object> map=new HashMap<String,Object>();
        List<ClientConfig> clients=repositoryService.loadClientConfigs(project);
        if(clients.size()>0){
            StringBuffer sb=new StringBuffer();
            int i=1;
            for(ClientConfig config:clients){
                if(i>1){
                    sb.append("<br>");
                }
                sb.append(config.getName()+"："+config.getClient());
                i++;
            }
            map.put("clientInfo", sb.toString());
        }
        writeObjectToJson(resp, map);
    }

    @ResponseBody
    @PostMapping("/urule/packageeditor/loadForTestVariableCategories")
    public List<VariableCategory> loadForTestVariableCategories(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        KnowledgeBase knowledgeBase = buildKnowledgeBase(req);
        List<VariableCategory> vcs=knowledgeBase.getResourceLibrary().getVariableCategories();
        httpSessionKnowledgeCache.put(req, VCS_KEY, vcs);
        return vcs;
    }

    @PostMapping("/urule/packageeditor/saveResourcePackages")
    public void saveResourcePackages(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        String project=req.getParameter("project");
        project=Utils.decodeURL(project);
        String path=project+"/"+ RepositoryServiceImpl.RES_PACKGE_FILE;
        String xml=req.getParameter("xml");
        User user= EnvironmentUtils.getLoginUser(new RequestContext(req,resp));
        repositoryService.saveFile(path, xml, false,null,user);
    }

    @PostMapping("/urule/packageeditor/doBatchTest")
    @SuppressWarnings("unchecked")
    public void doBatchTest(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        String flowId=req.getParameter("flowId");
        List<VariableCategory> vcs=(List<VariableCategory>)httpSessionKnowledgeCache.get(req, VCS_KEY);
        if(vcs==null){
            vcs=buildKnowledgeBase(req).getResourceLibrary().getVariableCategories();
        }
        Map<String,VariableCategory> vcmap=new HashMap<String,VariableCategory>();
        for(VariableCategory vc:vcs){
            vcmap.put(vc.getName(), vc);
        }
        List<Map<String,Object>> data=(List<Map<String,Object>>)httpSessionKnowledgeCache.get(req, IMPORT_EXCEL_DATA);
        if(data==null){
            throw new RuleException("Import excel data for test has expired,please import the excel and try again.");
        }
        Map<String,List<Object>> factMap=new HashMap<String,List<Object>>();
        for(Map<String,Object> map:data){
            String name=(String)map.get("name");
            VariableCategory vc=vcmap.get(name);
            if(vc==null){
                continue;
            }
            String clazz=vc.getClazz();
            List<Map<String,Object>> rowList=(List<Map<String,Object>>)map.get("data");
            List<Variable> variables=vc.getVariables();
            List<Object> factList=new ArrayList<Object>();
            for(Map<String,Object> rowMap:rowList){
                Object entity=null;
                if(vc.getName().equals(VariableCategory.PARAM_CATEGORY)){
                    entity=new HashMap<String,Object>();
                }else{
                    entity=new GeneralEntity(clazz);
                }
                buildObject(entity, rowMap,variables);
                factList.add(entity);
            }
            factMap.put(name, factList);
        }
        if(factMap.size()==0){
            throw new RuleException("Import data cannot match current knowledge package.");
        }
        int rowSize=0;
        List<String> keyList=new ArrayList<String>();
        for(String key:factMap.keySet()){
            keyList.add(key);
            List<Object> facts=factMap.get(key);
            if(facts.size()>rowSize){
                rowSize=facts.size();
            }
        }
        List<Map<String,Object>> resultList=new ArrayList<Map<String,Object>>();
        int mapSize=factMap.size();
        KnowledgeBase knowledgeBase=(KnowledgeBase)httpSessionKnowledgeCache.get(req, KB_KEY);
        KnowledgePackage knowledgePackage=knowledgeBase.getKnowledgePackage();
        KnowledgeSession session= KnowledgeSessionFactory.newKnowledgeSession(knowledgePackage);
        long start=System.currentTimeMillis();
        for(int i=0;i<rowSize;i++){
            Map<String,Object> parameterMap=null;
            for(int j=0;j<mapSize;j++){
                String categoryName=keyList.get(j);
                Object fact=fetchFact(factMap,keyList,j,i);
                if(fact==null){
                    continue;
                }
                if((fact instanceof Map) && !(fact instanceof GeneralEntity)){
                    parameterMap=(Map<String,Object>)fact;
                }else{
                    session.insert(fact);
                    buildResult(resultList,categoryName,fact);
                }
            }
            if(StringUtils.isNotEmpty(flowId)){
                if(parameterMap!=null){
                    session.startProcess(flowId,parameterMap);
                }else{
                    session.startProcess(flowId);
                }
            }else{
                if(parameterMap==null){
                    session.fireRules();
                }else{
                    session.fireRules(parameterMap);
                    Map<String,Object> p=new HashMap<String,Object>();
                    p.putAll(session.getParameters());
                    p.remove("return_to_");
                    buildResult(resultList,VariableCategory.PARAM_CATEGORY,p);
                }
            }
        }
        long end=System.currentTimeMillis();
        long elapse=end-start;
        StringBuffer sb=new StringBuffer();
        if(StringUtils.isNotEmpty(flowId)){
            sb.append("共执行规则流");
            sb.append("["+flowId+"]");
            sb.append(rowSize);
            sb.append("次,");
        }else{
            sb.append("共测试规则");
            sb.append(rowSize);
            sb.append("次,");
        }
        sb.append(""+"耗时："+elapse+"ms");
        Map<String,Object> result=new HashMap<String,Object>();
        result.put("info", sb.toString());
        result.put("data", resultList);
        writeObjectToJson(resp, result);
    }

    @PostMapping("/urule/packageeditor/doTest")
    @SuppressWarnings({ "unchecked"})
    public void doTest(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        String data=req.getParameter("data");
        ObjectMapper mapper=new ObjectMapper();
        List<Map<String,Object>> list=mapper.readValue(data, ArrayList.class);
        List<VariableCategory> variableCategories=mapToVariableCategories(list);
        Map<VariableCategory,Object> facts=new HashMap<VariableCategory,Object>();
        for(VariableCategory vc:variableCategories){
            String clazz=vc.getClazz();
            Object entity=null;
            if(vc.getName().equals(VariableCategory.PARAM_CATEGORY)){
                entity=new HashMap<String,Object>();
            }else{
                entity=new GeneralEntity(clazz);
            }
            for(Variable var:vc.getVariables()){
                buildObject(entity, var);
            }
            facts.put(vc,entity);
        }
        String flowId=req.getParameter("flowId");
        long start=System.currentTimeMillis();
        KnowledgeBase knowledgeBase=(KnowledgeBase)httpSessionKnowledgeCache.get(req, KB_KEY);
        if(knowledgeBase==null){
            knowledgeBase=buildKnowledgeBase(req);
        }
        KnowledgePackage knowledgePackage=knowledgeBase.getKnowledgePackage();
        KnowledgeSession session=KnowledgeSessionFactory.newKnowledgeSession(knowledgePackage);
        Map<String,Object> parameters=null;
        for(Object obj:facts.values()){
            if(!(obj instanceof GeneralEntity) && (obj instanceof HashMap)){
                parameters=(Map<String,Object>)obj;
            }else{
                session.insert(obj);
            }
        }
        ExecutionResponse response=null;
        if(StringUtils.isNotEmpty(flowId)){
            if(parameters!=null){
                response=session.startProcess(flowId,parameters);
            }else{
                response=session.startProcess(flowId);
            }
        }else{
            if(parameters==null){
                response=session.fireRules();
            }else{
                response=session.fireRules(parameters);
            }
        }
        for(VariableCategory vc:facts.keySet()){
            Object obj=facts.get(vc);
            if(obj==null){
                continue;
            }
            if(obj instanceof Map && !(obj instanceof GeneralEntity)){
                obj=session.getParameters();
            }
            for(Variable var:vc.getVariables()){
                buildVariableValue(obj, var);
            }
        }
        long end=System.currentTimeMillis();
        long elapse=end-start;
        session.writeLogFile();
        ExecutionResponseImpl res=(ExecutionResponseImpl)response;
        List<RuleInfo> firedRules=res.getFiredRules();
        List<RuleInfo> matchedRules=res.getMatchedRules();
        StringBuffer sb=new StringBuffer();
        sb.append("耗时："+elapse+"ms");
        if(StringUtils.isEmpty(flowId)){
            sb.append("，");
            sb.append("匹配的规则共"+matchedRules.size()+"个");
            if(matchedRules.size()>0){
                buildRulesName(matchedRules, sb);
            }
            sb.append("；");
            sb.append("触发的规则共"+firedRules.size()+"个");
            buildRulesName(firedRules, sb);
        }
        Map<String,Object> resultMap=new HashMap<String,Object>();
        resultMap.put("info", sb.toString());
        resultMap.put("data", variableCategories);
        writeObjectToJson(resp, resultMap);
    }

    @SuppressWarnings("resource")
    private List<Map<String,Object>> parseExcel(InputStream stream) throws Exception {
        List<Map<String,Object>> mapList=new ArrayList<Map<String,Object>>();
        XSSFWorkbook wb = new XSSFWorkbook(stream);
        for (int i = 0; i < wb.getNumberOfSheets(); i++) {
            XSSFSheet sheet = wb.getSheetAt(i);
            if (sheet == null) {
                continue;
            }
            String name = sheet.getSheetName();
            Map<String,Object> map=new HashMap<String,Object>();
            map.put("name",name);
            map.put("data", buildVariables(sheet));
            mapList.add(map);
        }
        return mapList;
    }


    @SuppressWarnings("deprecation")
    private List<Map<String,String>> buildVariables(XSSFSheet sheet){
        Map<Integer,String> headerMap=new HashMap<Integer,String>();
        List<Map<String,String>> mapList=new ArrayList<Map<String,String>>();
        int totalRow=sheet.getLastRowNum();
        XSSFRow headerRow=sheet.getRow(0);
        int totalColumn=headerRow.getLastCellNum();
        Map<String,String> noDataRowMap=new HashMap<String,String>();
        for(int i=0;i<totalColumn;i++){
            XSSFCell cell=headerRow.getCell(i);
            String value=cell.getStringCellValue();
            headerMap.put(i, value);
            String headerName=value.replaceAll("\\.", "-");
            noDataRowMap.put(headerName, null);
        }
        for(int i=1;i<=totalRow;i++){
            XSSFRow row=sheet.getRow(i);
            if(row==null){
                continue;
            }
            Map<String,String> map=new HashMap<String,String>();
            mapList.add(map);
            for(int j=0;j<totalColumn;j++){
                XSSFCell cell=row.getCell(j);
                String headerName=headerMap.get(j);
                if(headerName==null){
                    continue;
                }
                if(cell==null){
                    headerName=headerName.replaceAll("\\.", "-");
                    map.put(headerName, "");
                }else{
                    String value="";
                    int cellType=cell.getCellType();
                    switch(cellType){
                        case Cell.CELL_TYPE_STRING:
                            value=cell.getStringCellValue();
                            break;
                        case Cell.CELL_TYPE_BLANK:
                            value="";
                            break;
                        case Cell.CELL_TYPE_BOOLEAN:
                            value=String.valueOf(cell.getBooleanCellValue());
                            break;
                        case Cell.CELL_TYPE_NUMERIC:
                            value=String.valueOf(cell.getNumericCellValue());
                            break;
                        case Cell.CELL_TYPE_ERROR:
                            value="";
                            break;
                        case Cell.CELL_TYPE_FORMULA:
                            value=cell.getCellFormula();
                            break;
                    }
                    if(value==null){
                        value="";
                    }
                    headerName=headerName.replaceAll("\\.", "-");
                    map.put(headerName, value);
                }
            }
        }
        if(mapList.size()==0){
            mapList.add(noDataRowMap);
        }
        return mapList;
    }

    @SuppressWarnings("unchecked")
    private void buildResult(List<Map<String,Object>> list,String categoryName,Object fact){
        List<Object> rowList=null;
        for(Map<String,Object> map:list){
            if(map.get("name").equals(categoryName)){
                rowList=(List<Object>)map.get("data");
                break;
            }
        }
        if(rowList==null){
            rowList=new ArrayList<Object>();
            Map<String,Object> dataMap=new HashMap<String,Object>();
            dataMap.put("name", categoryName);
            dataMap.put("data", rowList);
            dataMap.put("id", UUID.randomUUID().toString());
            list.add(dataMap);
        }
        rowList.add(fact);
    }

    private Object fetchFact(Map<String,List<Object>> factMap,List<String> keyList,int i,int objectIndex){
        if(i>keyList.size()){
            return null;
        }
        String name=keyList.get(i);
        List<Object> factList = factMap.get(name);
        if(factList==null){
            return null;
        }
        if(objectIndex>=factList.size()){
            return null;
        }
        return factList.get(objectIndex);
    }


    private void buildObject(Object obj,Map<String,Object> map,List<Variable> variables){
        for(String name:map.keySet()){
            name=name.replaceAll("-", "\\.");
            if(name.indexOf(".")!=-1){
                instanceChildObject(obj,name);
            }
            Object value=map.get(name);
            Variable var=null;
            for(Variable variable:variables){
                if(name.equals(variable.getLabel()) || name.equals(variable.getName())){
                    var=variable;
                    break;
                }
            }
            if(var==null){
                throw new RuleException("Variable ["+name+"] not exist.");
            }
            Datatype type=var.getType();
            if(type.equals(Datatype.List) || type.equals(Datatype.Set) || type.equals(Datatype.Map)){
                continue;
            }
            value=type.convert(value);
            Utils.setObjectProperty(obj, var.getName(), value);
        }
    }

    private boolean pushKnowledgePackage(String packageId,String content,String client){
        HttpURLConnection connection=null;
        try{
            if(client.endsWith("/")){
                client=client.substring(0,client.length()-1);
            }
            String clientUrl=client+ KnowledgePackageReceiverController.URL;
            content="{\"packageId\":\""+ URLEncoder.encode(packageId, "utf-8")+"\",\"content\":\""+URLEncoder.encode(content, "utf-8")+"\"}";
            URL url=new URL(clientUrl);
            connection=(HttpURLConnection)url.openConnection();
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Charset", "UTF-8");
            connection.setRequestProperty("Accept-Charset", "utf-8");
            connection.setRequestProperty("Content-Type", "application/json");
            connection.setUseCaches(false);
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.connect();
            OutputStream outputStream=connection.getOutputStream();
            DataOutputStream wr = new DataOutputStream (connection.getOutputStream());
            wr.writeBytes(content);
            wr.flush();
            wr.close();
            if (connection.getResponseCode() >= 300) {
                return false;
            }
            InputStream inputStream=connection.getInputStream();
            String result= IOUtils.toString(inputStream);
            outputStream.close();
            inputStream.close();
            if(!result.equals("ok")){
                return false;
            }
            return true;
        }catch(Exception ex){
            ex.printStackTrace();
            return false;
        }finally {
            if(connection!=null){
                connection.disconnect();
            }
        }
    }

    @SuppressWarnings("unchecked")
    private List<VariableCategory> mapToVariableCategories(List<Map<String,Object>> mapList){
        List<VariableCategory> list=new ArrayList<VariableCategory>();
        for(Map<String,Object> map:mapList){
            VariableCategory category=new VariableCategory();
            list.add(category);
            for(String key:map.keySet()){
                if(key.equals("name")){
                    category.setName((String)map.get(key));
                }else if(key.equals("clazz")){
                    category.setClazz((String)map.get(key));
                }else if(key.equals("variables")){
                    List<Map<String,Object>> variables=(List<Map<String,Object>>)map.get(key);
                    if(variables!=null){
                        for(Map<String,Object> m:variables){
                            Variable var=new Variable();
                            category.addVariable(var);
                            for(String varName:m.keySet()){
                                if(varName.equals("name")){
                                    var.setName((String)m.get(varName));
                                }else if(varName.equals("label")){
                                    var.setLabel((String)m.get(varName));
                                }else if(varName.equals("type")){
                                    var.setType(Datatype.valueOf((String)m.get(varName)));
                                }else if(varName.equals("defaultValue")){
                                    var.setDefaultValue((String)m.get(varName));
                                }
                            }
                        }
                    }
                }
            }
        }
        return list;
    }

    private KnowledgeBase buildKnowledgeBase(HttpServletRequest req) throws IOException {
        String files=req.getParameter("files");
        files=Utils.decodeURL(files);
        ResourceBase resourceBase=knowledgeBuilder.newResourceBase();
        String[] paths=files.split(";");
        for(String path:paths){
            String[] subpaths=path.split(",");
            path=subpaths[0];
            String version=null;
            if(subpaths.length>1){
                version=subpaths[1];
            }
            resourceBase.addResource(path,version);
        }
        KnowledgeBase knowledgeBase=knowledgeBuilder.buildKnowledgeBase(resourceBase);
        httpSessionKnowledgeCache.remove(req, KB_KEY);
        httpSessionKnowledgeCache.put(req, KB_KEY, knowledgeBase);
        return knowledgeBase;
    }

    private void buildSheet(SXSSFWorkbook wb,VariableCategory vc,XSSFCellStyle style){
        String name=vc.getName();
        Sheet sheet=wb.createSheet(name);
        Row row=sheet.createRow(0);
        List<Variable> variables=vc.getVariables();
        for(int i=0;i<variables.size();i++){
            sheet.setColumnWidth(i,4000);
            Cell cell=row.createCell(i);
            Variable var=variables.get(i);
            cell.setCellValue(var.getLabel());
            cell.setCellStyle(style);
        }
    }

    private void buildObject(Object obj,Variable var){
        String name=var.getName();
        if(name.indexOf(".")!=-1){
            instanceChildObject(obj,name);
        }
        String defaultValue=var.getDefaultValue();
        if(StringUtils.isBlank(defaultValue)){
            return;
        }
        Datatype type=var.getType();
        if(type.equals(Datatype.List)){
            Utils.setObjectProperty(obj, name, buildList(defaultValue));
        }else if(type.equals(Datatype.Set)){
            Utils.setObjectProperty(obj, name, buildSet(defaultValue));
        }else if(type.equals(Datatype.Map)){
            return;
        }else{
            Object value=type.convert(defaultValue);
            Utils.setObjectProperty(obj, name, value);
        }
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    private List<GeneralEntity> buildList(String value){
        try {
            List<GeneralEntity> result=new ArrayList<GeneralEntity>();
            ObjectMapper mapper=new ObjectMapper();
            Map<String,Object> map=mapper.readValue(value, HashMap.class);
            if(map.containsKey("rows")){
                List<Object> list=(List<Object>)map.get("rows");
                for(Object obj:list){
                    if(obj instanceof Map){
                        GeneralEntity entity=new GeneralEntity((String)map.get("type"));
                        entity.putAll((Map)obj);
                        result.add(entity);
                    }
                }
                return result;
            }else{
                return null;
            }
        } catch (Exception e) {
            throw new RuleException(e);
        }
    }
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private Set<GeneralEntity> buildSet(String value){
        try {
            Set<GeneralEntity> result=new HashSet<GeneralEntity>();
            ObjectMapper mapper=new ObjectMapper();
            Map<String,Object> map=mapper.readValue(value, HashMap.class);
            if(map.containsKey("rows")){
                List<Object> list=(List<Object>)map.get("rows");
                for(Object obj:list){
                    if(obj instanceof Map){
                        GeneralEntity entity=new GeneralEntity((String)map.get("type"));
                        entity.putAll((Map)obj);
                        result.add(entity);
                    }
                }
                return result;
            }else{
                return null;
            }
        } catch (Exception e) {
            throw new RuleException(e);
        }
    }

    private void instanceChildObject(Object obj,String propertyName){
        int pointIndex=propertyName.indexOf(".");
        if(pointIndex==-1){
            return;
        }
        String name=propertyName.substring(0,pointIndex);
        propertyName=propertyName.substring(pointIndex+1);
        try {
            Object instance= PropertyUtils.getProperty(obj, name);
            if(instance!=null){
                instanceChildObject(instance,propertyName);
                return;
            }
            Object targetEntity=new GeneralEntity(name);
            PropertyUtils.setProperty(obj, name, targetEntity);
            instanceChildObject(targetEntity,propertyName);
        } catch (Exception e) {
            throw new RuleException(e);
        }
    }


    private void buildRulesName(List<RuleInfo> firedRules, StringBuffer sb) {
        sb.append("：");
        int i=0;
        for(RuleInfo rule:firedRules){
            if(i>0){
                sb.append("，");
            }
            sb.append(rule.getName());
            i++;
        }
    }

    private void buildVariableValue(Object object,Variable var){
        String name=var.getName();
        Object value=Utils.getObjectProperty(object, name);
        if(value!=null){
            Datatype type=var.getType();
            if(type.equals(Datatype.List) || type.equals(Datatype.Set)){
                //var.setDefaultValue(value.toString());
            }else{
                String str=type.convertObjectToString(value);
                var.setDefaultValue(str);
            }
        }
    }
}
